package com.miracle9.game.server.message;

import com.miracle9.game.util.MyApplicationContext;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.log4j.Logger;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jdom.Document;
import org.jdom.input.SAXBuilder;
import org.springframework.stereotype.Service;
import org.xml.sax.InputSource;
import wox.serial.SimpleReader;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.util.*;

@Service("socketHandler")
public class SocketHandler implements IoHandler {

    private Logger logger = Logger.getLogger(SocketHandler.class);
    public static Set<IoSession> D = new HashSet<>();
    final String KEY_ALGORITHM = "AES";
    final String algorithmStr = "AES/CBC/PKCS7Padding";
    private Key key;
    private Cipher cipher;
    public static String aIv = "dHjWxY506jk0K7W8";
    byte[] iv;

    public void exceptionCaught(IoSession session, Throwable arg1) throws Exception {
        arg1.printStackTrace();
        logger.info("error " + arg1.getMessage());
        session.close(true);
    }

    public void messageReceived(IoSession session, Object message) throws Exception {
        try {
            IoBuffer buffer = (IoBuffer) message;
            int dataLength = buffer.getInt();
            byte[] contentBytes = new byte[dataLength];
            buffer.get(contentBytes);

            contentBytes = jiemi2(contentBytes, session);

            String str = new String(contentBytes, "UTF-8");

            /*if(!str.contains("heart")) {
                logger.info(str);
            }*/

            if (!str.startsWith("{")) {
                StringReader sr = new StringReader(str);
                InputSource is = new InputSource(sr);
                Document doc = new SAXBuilder().build(is);
                SimpleReader r = new SimpleReader();

                Map map = (Map) r.read(doc.getRootElement());

                String url = (String) map.get("method");

                Object[] args = (Object[]) map.get("args");

                String argsStr = "arg：";
                for (Object o : args) {
                    argsStr = argsStr + o + ";";
                }
                String[] urls = url.split("/");
                String beanId = urls[0];
                String method = urls[1];
                if (!"heart".equalsIgnoreCase(method)) {
                    this.logger.info("rev：" + url);
                    this.logger.info(argsStr);
                }
                String version = (String) map.get("version");
                if ((version == null) || (com.miracle9.game.util.GameParameterAssist.aC.compareTo(version) > 0)) {
                    this.logger.info("disallow this client to send request,client version:" + version);
                    session.close(true);
                    return;
                }
                if (!"publicKey".equals(method)) {
                    long time = ((Long) map.get("time")).longValue();
                    if (System.currentTimeMillis() - time >= 5000L) {
                        this.logger.info("请求时间大于5秒");
                        session.close(true);
                        return;
                    }
                }
                Object[] args_login = new Object[args.length + 1];
                for (int i = 0; i < args.length; i++) {
                    args_login[i] = args[i];
                }
                args_login[(args_login.length - 1)] = session;
                args = args_login;

                Object service = MyApplicationContext.a().getBean(beanId);
                Method m = service.getClass().getDeclaredMethod(method, getClasses(args));
                Object result = m.invoke(service, args);
                if (result != null) {
                    AmfMessageSend.storageMethodOperating.notifyAllOperating(new StorageMethodEntity(session, method, new Object[]{result}));
                }
            } else {
                Map map = JSONObject.fromObject(str);
                String url = (String) map.get("method");
                JSONArray jsonlist = (JSONArray) map.get("args");
                Object[] args1 = jsonlist.toArray();

                String argsStr1 = "arg:";
                Object[] os;
                int argsStr1Len = (os = args1).length;
                for (int i = 0; i < argsStr1Len; i++) {
                    Object o = os[i];
                    argsStr1 = argsStr1 + o + ";";
                }
                String[] urls1 = url.split("/");
                String beanId = urls1[0];
                String method = urls1[1];
                if ("gcuserService".equalsIgnoreCase(beanId)) {
                    beanId = "userService";
                    if ("login".equalsIgnoreCase(method)) {
                        method = "userLogin";
                        Object[] args_new = new Object[args1.length + 1];
                        for (int i = 0; i < args1.length; i++) {
                            args_new[i] = args1[i];
                        }
                        args_new[args1.length] = "8.3.2";
                        args1 = args_new;
                    }
                    if ("getExpeGold".equalsIgnoreCase(method)) {
                        method = "addExpeGold";
                    }
                }
                if ("getNotice".equalsIgnoreCase(method)) {
                    beanId = "noticeService";
                }
                if ("updateUserPwd".equalsIgnoreCase(method)) {
                    method = "updatePassword";
                }
                if ("excharge".equalsIgnoreCase(method)) {
                    method = "exCharge";
                }
                if ("getRankList".equalsIgnoreCase(method)) {
                    beanId = "userTopService";
                }
                if ("getEmailList".equalsIgnoreCase(method)) {
                    method = "mailList";
                    beanId = "userMailService";
                }
                if ("checkVersion".equalsIgnoreCase(method)) {
                    method = "checkVersion";
                    beanId = "versionService";
                }
                if ("SecuritySet".equalsIgnoreCase(method)) {
                    Object mibao = new HashMap();

                    ((Map) mibao).put("success", Boolean.valueOf(true));
                    ((Map) mibao).put("message", "");
                    MessageSend.q.notifyAllOperating(new StorageMethodEntity(session, method, new Object[]{mibao}));
                    return;
                }
                if ("gcsecurityService".equalsIgnoreCase(beanId)) {
                    if (("SecuritySet".equalsIgnoreCase(method)) || ("securityCheck".equalsIgnoreCase(method))) {
                        Object result = new HashMap();
                        ((Map) result).put("success", Boolean.valueOf(false));
                        ((Map) result).put("message", "");
                        MessageSend.q.notifyAllOperating(new StorageMethodEntity(session, method, new Object[]{result}));
                        return;
                    }
                    if (com.miracle9.game.util.GameParameterAssist.bSupportSafeBox) {
                        beanId = "userService";
                    } else {
                        Object result = new HashMap();
                        ((Map) result).put("success", Boolean.valueOf(false));
                        ((Map) result).put("msgCode", Integer.valueOf(0));
                        MessageSend.q.notifyAllOperating(new StorageMethodEntity(session, method, new Object[]{result}));
                        return;
                    }
                }
                String version = (String) map.get("version");
                if ((version == null) || (com.miracle9.game.util.GameParameterAssist.aC.compareTo(version) > 0)) {
                    this.logger.info("disallow this client to send request,client version:" + version);
                    session.close(true);
                    return;
                }
                if ((!"publicKey".equals(method)) && (!"checkVersion".equals(method))) {
                    long time = ((Long) map.get("time")).longValue();
                    if (System.currentTimeMillis() - time >= 5000L) {
                        this.logger.info("请求时间大于5秒");
                        session.close(true);
                        return;
                    }
                }
                Object[] args_login = new Object[args1.length + 1];
                for (int i = 0; i < args1.length; i++) {
                    args_login[i] = args1[i];
                }
                args_login[(args_login.length - 1)] = session;
                args1 = args_login;

                Object service = MyApplicationContext.a().getBean(beanId);
                Method m = service.getClass().getDeclaredMethod(method, getClasses(args1));
                String argstr = "";
                for (Object o : args1) {
                    argstr += o + ",";
                }
                //logger.info("invoke："+beanId+"，method："+method+"，args："+argstr);
                Object result = m.invoke(service, args1);
                if (result != null) {
                    if ("userLogin".equalsIgnoreCase(method)) {
                        method = "login";
                    }
                    if ("mailList".equalsIgnoreCase(method)) {
                        method = "getEmailList";
                    }
                    if ("mailList".equalsIgnoreCase(method)) {
                        method = "getEmailList";
                    }
                    if ("exCharge".equalsIgnoreCase(method)) {
                        method = "excharge";
                    }
                    if ("updatePassword".equalsIgnoreCase(method)) {
                        method = "updateUserPwd";
                    }
                    if (("userService".equalsIgnoreCase(beanId)) && ("addExpeGold".equalsIgnoreCase(method))) {
                        method = "getExpeGold";
                    }
                    MessageSend.q.notifyAllOperating(new StorageMethodEntity(session, method, new Object[]{result}));
                }
            }
        } catch (Exception e) {
            this.logger.info("eeee:", e);
            this.logger.info("eeee info:" + getExceptionAllinformation(e));
        }
    }

    private Class<?>[] getClasses(Object[] args) {
        try {
            Class[] classArray = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                classArray[i] = args[i].getClass();
                if ((args[i] instanceof Integer)) {
                    classArray[i] = Integer.TYPE;
                } else if ((args[i] instanceof Boolean)) {
                    classArray[i] = Boolean.TYPE;
                } else if ((args[i] instanceof Map)) {
                    classArray[i] = Map.class;
                } else if ((args[i] instanceof Long)) {
                    classArray[i] = Long.TYPE;
                } else if ((args[i] instanceof Double)) {
                    classArray[i] = Double.TYPE;
                } else if ((args[i] instanceof IoSession)) {
                    classArray[i] = IoSession.class;
                } else if ((args[i] instanceof Float)) {
                    classArray[i] = Float.TYPE;
                }
            }
            return classArray;
        } catch (Exception e) {
            this.logger.error("", e);
        }
        return null;
    }

    public void messageSent(IoSession arg0, Object arg1) throws Exception {
    }

    public void sessionClosed(IoSession session) throws Exception {
        com.miracle9.game.util.GameParameterAssist.bg.remove(session);
        com.miracle9.game.util.GameParameterAssist.bd.remove(session);

        D.remove(session);
        Object obj = session.getAttribute("id");
        if (obj != null) {
            com.miracle9.game.util.GameParameterAssist.ba.remove(Integer.valueOf(Integer.parseInt(obj.toString())));
        }
        if (obj != null) {
            if (com.miracle9.game.util.GameParameterAssist.aR.containsKey(Integer.valueOf(Integer.parseInt(obj.toString())))) {
                com.miracle9.game.util.GameParameterAssist.aR.remove(Integer.valueOf(Integer.parseInt(obj.toString())));
            }
        }
        this.logger.info("sessionClosed...");
    }

    public void sessionCreated(IoSession arg0) throws Exception {
    }

    public void sessionIdle(IoSession session, IdleStatus arg1) throws Exception {
        session.close(true);
    }

    public void sessionOpened(IoSession session) throws Exception {
        String clientIP = ((InetSocketAddress) session.getRemoteAddress()).getAddress().getHostAddress();
        session.setAttribute("ip", clientIP);
        D.add(session);
        com.miracle9.game.util.GameParameterAssist.bg.put(session, Long.valueOf(System.currentTimeMillis()));
    }

    public byte[] jiemi2(byte[] bytes, IoSession session) {
        if (com.miracle9.game.util.GameParameterAssist.bd.containsKey(session)) {
            String key2 = com.miracle9.game.util.GameParameterAssist.bd.get(session);
            try {
                byte[] encrypted = decrypt(bytes, key2.getBytes("utf-8"));
                String ss = "";
                for (int i = 0; i < encrypted.length; i++) {
                    ss = ss + encrypted[i] + ",";
                }
                return encrypted;
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        return bytes;
    }

    public void init(byte[] keyBytes) {
        int base = 16;
        if (keyBytes.length % base != 0) {
            int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
            keyBytes = temp;
        }
        Security.addProvider(new BouncyCastleProvider());
        this.key = new SecretKeySpec(keyBytes, "AES");
        try {
            this.cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
        }
    }

    public byte[] encrypt(byte[] content, byte[] keyBytes) {
        byte[] encryptedText = (byte[]) null;
        init(keyBytes);
        try {
            this.iv = aIv.getBytes("utf-8");
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }
        try {
            this.cipher.init(1, this.key, new IvParameterSpec(this.iv));
            encryptedText = this.cipher.doFinal(content);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encryptedText;
    }

    public byte[] decrypt(byte[] encryptedData, byte[] keyBytes) {
        byte[] encryptedText = (byte[]) null;
        init(keyBytes);
        try {
            this.iv = aIv.getBytes("utf-8");
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }
        try {
            this.cipher.init(2, this.key, new IvParameterSpec(this.iv));
            encryptedText = this.cipher.doFinal(encryptedData);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encryptedText;
    }

    public static String getExceptionAllinformation(Exception ex) {
        String sOut = "";
        StackTraceElement[] trace = ex.getStackTrace();
        for (StackTraceElement s : trace) {
            sOut = sOut + "\tat " + s + "\r\n";
        }
        return sOut;
    }

}
